package com.constellio.model.conf.ldap; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.regex.PatternSyntaxException; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.joda.time.Duration; import org.joda.time.format.PeriodFormatter; import org.joda.time.format.PeriodFormatterBuilder; import com.constellio.data.dao.managers.StatefulService; import com.constellio.data.dao.managers.config.ConfigManager; import com.constellio.data.dao.managers.config.PropertiesAlteration; import com.constellio.model.conf.PropertiesModelLayerConfigurationRuntimeException; import com.constellio.model.conf.ldap.config.AzureADServerConfig; import com.constellio.model.conf.ldap.config.AzureADUserSynchConfig; import com.constellio.model.conf.ldap.config.LDAPServerConfiguration; import com.constellio.model.conf.ldap.config.LDAPUserSyncConfiguration; import com.constellio.model.conf.ldap.services.LDAPServicesImpl; import com.constellio.model.services.encrypt.EncryptionServices; import com.constellio.model.services.factories.ModelLayerFactory; import com.constellio.model.services.users.sync.LDAPFastBind; import com.constellio.model.services.users.sync.RuntimeNamingException; public class LDAPConfigurationManager implements StatefulService { private static final String LDAP_CONFIGS = "ldapConfigs.properties"; public static final long MIN_DURATION = 1000 * 60 * 10;//10mns private final ModelLayerFactory modelLayerFactory; LDAPUserSyncConfiguration userSyncConfiguration; LDAPServerConfiguration serverConfiguration; EncryptionServices encryptionServices; ConfigManager configManager; Date nextUsersSyncFireTime; public LDAPConfigurationManager(ModelLayerFactory modelLayerFactory, ConfigManager configManager) { this.configManager = configManager; this.modelLayerFactory = modelLayerFactory; configManager.createPropertiesDocumentIfInexistent(LDAP_CONFIGS, new PropertiesAlteration() { @Override public void alter(Map<String, String> properties) { //Default values } }); } @Override public void initialize() { } @Override public void close() { } public void saveLDAPConfiguration(final LDAPServerConfiguration ldapServerConfiguration, final LDAPUserSyncConfiguration ldapUserSyncConfiguration) { saveLDAPConfiguration(ldapServerConfiguration, ldapUserSyncConfiguration, true); } public void saveLDAPConfiguration(final LDAPServerConfiguration ldapServerConfiguration, final LDAPUserSyncConfiguration ldapUserSyncConfiguration, boolean validateBeforeSave) { if (validateBeforeSave) { validateLDAPConfiguration(ldapServerConfiguration, ldapUserSyncConfiguration); } configManager.updateProperties(LDAP_CONFIGS, new PropertiesAlteration() { @Override public void alter(Map<String, String> properties) { if (ldapServerConfiguration.getLdapAuthenticationActive()) { properties.put("ldap.authentication.active", "" + ldapServerConfiguration.getLdapAuthenticationActive()); } else { properties.put("ldap.authentication.active", "" + false); } LDAPDirectoryType directoryType; if (ldapServerConfiguration.getDirectoryType() != null) { properties .put("ldap.serverConfiguration.directoryType", ldapServerConfiguration.getDirectoryType().getCode()); directoryType = ldapServerConfiguration.getDirectoryType(); } else { directoryType = LDAPDirectoryType.ACTIVE_DIRECTORY; } if (directoryType == LDAPDirectoryType.AZURE_AD) { properties.put("ldap.serverConfiguration.authorityTenantId", ldapServerConfiguration.getTenantName()); properties.put("ldap.serverConfiguration.clientId", ldapServerConfiguration.getClientId()); properties.put("ldap.syncConfiguration.clientId", ldapUserSyncConfiguration.getClientId()); properties.put("ldap.syncConfiguration.applicationKey", ldapUserSyncConfiguration.getClientSecret()); if (ldapUserSyncConfiguration.getGroupsFilter() != null) { properties.put("ldap.syncConfiguration.groupsFilter", ldapUserSyncConfiguration.getGroupsFilter()); } if (ldapUserSyncConfiguration.getUsersFilter() != null) { properties.put("ldap.syncConfiguration.usersFilter", ldapUserSyncConfiguration.getUsersFilter()); } if (!CollectionUtils.isEmpty(ldapUserSyncConfiguration.getUserGroups())) { properties.put("ldap.syncConfiguration.userGroups.sharpSV", joinWithSharp(ldapUserSyncConfiguration.getUserGroups())); } } else { properties.put("ldap.serverConfiguration.urls.sharpSV", joinWithSharp(ldapServerConfiguration.getUrls())); properties .put("ldap.serverConfiguration.domains.sharpSV", joinWithSharp(ldapServerConfiguration.getDomains())); if (ldapServerConfiguration.getFollowReferences() != null && ldapServerConfiguration.getFollowReferences()) { properties.put("ldap.serverConfiguration.followReferences", "" + true); } else { properties.put("ldap.serverConfiguration.followReferences", "" + false); } if (ldapUserSyncConfiguration.getUser() != null) { properties.put("ldap.syncConfiguration.user.login", ldapUserSyncConfiguration.getUser()); } String password = ldapUserSyncConfiguration.getPassword(); if (password != null) { String encryptedPassword = password; if (StringUtils.isNotBlank(password)) { encryptedPassword = modelLayerFactory.newEncryptionServices().encrypt(password); } properties.put("ldap.syncConfiguration.user.password", encryptedPassword); } if (ldapUserSyncConfiguration.getGroupBaseContextList() != null) { properties.put("ldap.syncConfiguration.groupsBaseContextList.sharpSV", joinWithSharp(ldapUserSyncConfiguration.getGroupBaseContextList())); } if (ldapUserSyncConfiguration.getUsersWithoutGroupsBaseContextList() != null) { properties.put("ldap.syncConfiguration.usersWithoutGroupsBaseContextList.sharpSV", joinWithSharp(ldapUserSyncConfiguration.getUsersWithoutGroupsBaseContextList())); } if (ldapUserSyncConfiguration.getUserFilterGroupsList() != null) { properties.put("ldap.syncConfiguration.userFilterGroupsList.sharpSV", joinWithSharp(ldapUserSyncConfiguration.getUserFilterGroupsList())); } properties.put("ldap.syncConfiguration.membershipAutomaticDerivationActivated", Boolean.toString(ldapUserSyncConfiguration.isMembershipAutomaticDerivationActivated())); } properties.put("ldap.syncConfiguration.selectedCollectionsCodes.sharpSV", joinWithSharp(ldapUserSyncConfiguration.getSelectedCollectionsCodes())); if (ldapUserSyncConfiguration.getUsersFilterAcceptanceRegex() != null) { properties.put("ldap.syncConfiguration.userFilter.acceptedRegex", ldapUserSyncConfiguration.getUsersFilterAcceptanceRegex()); } if (ldapUserSyncConfiguration.getUsersFilterRejectionRegex() != null) { properties.put("ldap.syncConfiguration.userFilter.rejectedRegex", ldapUserSyncConfiguration.getUsersFilterRejectionRegex()); } if (ldapUserSyncConfiguration.getGroupsFilterAcceptanceRegex() != null) { properties.put("ldap.syncConfiguration.groupFilter.acceptedRegex", ldapUserSyncConfiguration.getGroupsFilterAcceptanceRegex()); } if (ldapUserSyncConfiguration.getGroupsFilterRejectionRegex() != null) { properties.put("ldap.syncConfiguration.groupFilter.rejectedRegex", ldapUserSyncConfiguration.getGroupsFilterRejectionRegex()); } if (ldapUserSyncConfiguration.getScheduleTime() == null || ldapUserSyncConfiguration.getScheduleTime().isEmpty()) { properties.remove("ldap.syncConfiguration.schedule.time.sharpSV"); } else { properties.put("ldap.syncConfiguration.schedule.time.sharpSV", joinWithSharp(ldapUserSyncConfiguration.getScheduleTime())); } if (ldapUserSyncConfiguration.getDurationBetweenExecution() != null) { long durationInMilli = ldapUserSyncConfiguration.getDurationBetweenExecution().getMillis(); if (durationInMilli >= MIN_DURATION) { properties.put("ldap.syncConfiguration.durationBetweenExecution", format(durationInMilli) + ""); } else if (durationInMilli == 0L) { properties.remove("ldap.syncConfiguration.durationBetweenExecution"); } else { throw new TooShortDurationRuntimeException(ldapUserSyncConfiguration.getDurationBetweenExecution()); } } } }); modelLayerFactory.getLdapAuthenticationService().reloadServiceConfiguration(); modelLayerFactory.getLdapUserSyncManager().reloadLDAPUserSynchConfiguration(); } /*private void init(boolean decryptPassword) { if (!decryptPassword) { this.userSyncConfiguration = getLDAPUserSyncConfiguration(false); this.serverConfiguration = getLDAPServerConfiguration(); } if (this.encryptionServices != null) { return; } try { this.encryptionServices = modelLayerFactory.newEncryptionServices(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } catch (InvalidKeySpecException e) { throw new RuntimeException(e); } this.userSyncConfiguration = getLDAPUserSyncConfiguration(true); this.serverConfiguration = getLDAPServerConfiguration(); }*/ private String format(long millis) { //format (\\d*d)(\\d*h)(\\d*mn) (d == day) long seconds = millis / 1000; long mns = seconds / 60; long hours = mns / 60; mns = mns % 60; long days = hours / 24; hours = hours % 24; return days + "d" + hours + "h" + mns + "mn"; } private String joinWithSharp(List<String> list) { if (list == null) { return ""; } return StringUtils.join(list, "#"); } private void validateLDAPConfiguration(LDAPServerConfiguration configs, LDAPUserSyncConfiguration ldapUserSyncConfiguration) { Boolean authenticationActive = configs.getLdapAuthenticationActive(); if (authenticationActive) { LDAPDirectoryType directoryType = configs.getDirectoryType(); if (directoryType == LDAPDirectoryType.AZURE_AD) { validateAzurConfig(configs, ldapUserSyncConfiguration); } else { validateADAndEDirectoryConfiguration(configs, ldapUserSyncConfiguration); } if (ldapUserSyncConfiguration.getDurationBetweenExecution() != null && ldapUserSyncConfiguration.getDurationBetweenExecution().getMillis() != 0L) { if (ldapUserSyncConfiguration.getDurationBetweenExecution().getMillis() < MIN_DURATION) { throw new TooShortDurationRuntimeException(ldapUserSyncConfiguration.getDurationBetweenExecution()); } } } } private void validateADAndEDirectoryConfiguration(LDAPServerConfiguration configs, LDAPUserSyncConfiguration ldapUserSyncConfiguration) { boolean activeDirectory = configs.getDirectoryType().equals(LDAPDirectoryType.ACTIVE_DIRECTORY); for (String url : configs.getUrls()) { try { LDAPFastBind fastBind = new LDAPFastBind(url, configs.getFollowReferences(), activeDirectory); fastBind.close(); } catch (RuntimeNamingException e) { throw new InvalidUrlRuntimeException(url, e.getMessage()); } } if (configs.getDomains() == null || configs.getDomains().isEmpty()) { throw new EmptyDomainsRuntimeException(); } if (configs.getUrls() == null || configs.getUrls().isEmpty()) { throw new EmptyUrlsRuntimeException(); } LDAPServicesImpl ldapServices = new LDAPServicesImpl(); for (String url : configs.getUrls()) { ldapServices.connectToLDAP(configs.getDomains(), url, ldapUserSyncConfiguration.getUser(), ldapUserSyncConfiguration.getPassword(), configs.getFollowReferences(), activeDirectory); } } private void validateAzurConfig(LDAPServerConfiguration configs, LDAPUserSyncConfiguration ldapUserSyncConfiguration) { //TODO } public LDAPServerConfiguration getLDAPServerConfiguration() { Map<String, String> configs = configManager.getProperties(LDAP_CONFIGS).getProperties(); LDAPDirectoryType directoryType = getLDAPDirectoryType(configs); Boolean active = getBooleanValue(configs, "ldap.authentication.active", false); if (directoryType == LDAPDirectoryType.AZURE_AD) { String authorityTanentId = getString(configs, "ldap.serverConfiguration.authorityTenantId", null); String clientId = getString(configs, "ldap.serverConfiguration.clientId", null); AzureADServerConfig serverConf = new AzureADServerConfig().setAuthorityTenantId(authorityTanentId).setClientId(clientId); return new LDAPServerConfiguration(serverConf, active); } else { List<String> urls = getSharpSeparatedValuesWithoutBlanks(configs, "ldap.serverConfiguration.urls.sharpSV", new ArrayList<String>()); List<String> domains = getSharpSeparatedValuesWithoutBlanks(configs, "ldap.serverConfiguration.domains.sharpSV", new ArrayList<String>()); Boolean followReferences = getBooleanValue(configs, "ldap.serverConfiguration.followReferences", false); return new LDAPServerConfiguration(urls, domains, directoryType, active, followReferences); } } public LDAPUserSyncConfiguration getLDAPUserSyncConfiguration() { return getLDAPUserSyncConfiguration(true); } public LDAPUserSyncConfiguration getLDAPUserSyncConfiguration(boolean decryptPassword) { Map<String, String> configs = configManager.getProperties(LDAP_CONFIGS).getProperties(); RegexFilter userFilter = newRegexFilter(configs, "ldap.syncConfiguration.userFilter.acceptedRegex", "ldap.syncConfiguration.userFilter.rejectedRegex"); RegexFilter groupFilter = newRegexFilter(configs, "ldap.syncConfiguration.groupFilter.acceptedRegex", "ldap.syncConfiguration.groupFilter.rejectedRegex"); Duration durationBetweenExecution = newDuration(configs, "ldap.syncConfiguration.durationBetweenExecution"); List<String> scheduleTimeList = getSharpSeparatedValuesWithoutBlanks(configs, "ldap.syncConfiguration.schedule.time.sharpSV", new ArrayList<String>()); List<String> selectedCollections = getSharpSeparatedValuesWithoutBlanks(configs, "ldap.syncConfiguration.selectedCollectionsCodes.sharpSV", new ArrayList<String>()); boolean membershipAutomaticDerivationActivated = getBooleanValue(configs, "ldap.syncConfiguration.membershipAutomaticDerivationActivated", true); LDAPDirectoryType directoryType = getLDAPDirectoryType(configs); if (directoryType == LDAPDirectoryType.AZURE_AD) { String applicationKey = getString(configs, "ldap.syncConfiguration.applicationKey", null); String synchClientId = getString(configs, "ldap.syncConfiguration.clientId", null); String groupsFilter = getString(configs, "ldap.syncConfiguration.groupsFilter", null); String usersFilter = getString(configs, "ldap.syncConfiguration.usersFilter", null); List<String> userGroups = getSharpSeparatedValuesWithoutBlanks(configs, "ldap.syncConfiguration.userGroups.sharpSV", null); AzureADUserSynchConfig azurConf = new AzureADUserSynchConfig() .setApplicationKey(applicationKey) .setClientId(synchClientId) .setGroupsFilter(groupsFilter) .setUsersFilter(usersFilter) .setUserGroups(userGroups); return new LDAPUserSyncConfiguration(azurConf, userFilter, groupFilter, durationBetweenExecution, selectedCollections); } else { String user = getString(configs, "ldap.syncConfiguration.user.login", null); List<String> groupBaseContextList = getSharpSeparatedValuesWithoutBlanks(configs, "ldap.syncConfiguration.groupsBaseContextList.sharpSV", new ArrayList<String>()); List<String> usersWithoutGroupsBaseContextList = getSharpSeparatedValuesWithoutBlanks(configs, "ldap.syncConfiguration.usersWithoutGroupsBaseContextList.sharpSV", new ArrayList<String>()); List<String> userFilterGroupsList = getSharpSeparatedValuesWithoutBlanks(configs, "ldap.syncConfiguration.userFilterGroupsList.sharpSV", new ArrayList<String>()); String password = getString(configs, "ldap.syncConfiguration.user.password", ""); if (decryptPassword) { if (encryptionServices == null) { encryptionServices = modelLayerFactory.newEncryptionServices(); } password = encryptionServices.decrypt(password); } return new LDAPUserSyncConfiguration(user, password, userFilter, groupFilter, durationBetweenExecution, scheduleTimeList, groupBaseContextList, usersWithoutGroupsBaseContextList, userFilterGroupsList, membershipAutomaticDerivationActivated, selectedCollections); } } public boolean isLDAPAuthentication() { Map<String, String> configs = configManager.getProperties(LDAP_CONFIGS).getProperties(); return getBooleanValue(configs, "ldap.authentication.active", false); } private boolean getBooleanValue(Map<String, String> configs, String key, boolean defaultValue) { String returnValueAsString = getString(configs, key, defaultValue + ""); if (!returnValueAsString.toLowerCase().matches("true|false")) { throw new PropertiesModelLayerConfigurationRuntimeException.PropertiesModelLayerConfigurationRuntimeException_NotABooleanValue( key, returnValueAsString); } return Boolean.valueOf(returnValueAsString); } private String getString(Map<String, String> configs, String key, String defaultValue) { String value = configs.get(key); return value == null ? defaultValue : value; } private LDAPDirectoryType getLDAPDirectoryType(Map<String, String> configs) { String directoryTypeString = getString(configs, "ldap.serverConfiguration.directoryType", LDAPDirectoryType.ACTIVE_DIRECTORY.getCode()).toLowerCase(); if (StringUtils.isBlank(directoryTypeString) || directoryTypeString.equals(LDAPDirectoryType.ACTIVE_DIRECTORY.getCode().toLowerCase())) { return LDAPDirectoryType.ACTIVE_DIRECTORY; } else if (directoryTypeString.equals(LDAPDirectoryType.E_DIRECTORY.getCode().toLowerCase())) { return LDAPDirectoryType.E_DIRECTORY; } else if (directoryTypeString.equals(LDAPDirectoryType.AZURE_AD.getCode().toLowerCase())) { return LDAPDirectoryType.AZURE_AD; } else { throw new PropertiesModelLayerConfigurationRuntimeException.PropertiesModelLayerConfigurationRuntimeException_InvalidLdapType( directoryTypeString); } } private List<String> getSharpSeparatedValuesWithoutBlanks(Map<String, String> configs, String key, ArrayList<String> defaultValues) { String allValuesString = getString(configs, key, ""); String[] allVales = StringUtils.split(allValuesString, "#"); if (allVales.length == 0) { return defaultValues; } else { List<String> returnValues = new ArrayList<>(); for (String currentValue : allVales) { returnValues.add(currentValue.trim()); } return returnValues; } } private Duration newDuration(Map<String, String> configs, String key) { String durationString = getString(configs, key, null); if (durationString != null) { //format (\\d*d)(\\d*h)(\\d*mn) (d == day) PeriodFormatter formatter = new PeriodFormatterBuilder() .appendDays() .appendLiteral("d") .appendHours() .appendLiteral("h") .appendMinutes() .appendLiteral("mn") .toFormatter(); Duration duration = formatter.parsePeriod(durationString).toStandardDuration(); return duration; } return null; } private RegexFilter newRegexFilter(Map<String, String> configs, String acceptedRegexKey, String rejectedRegexKey) { String acceptedRegex = getString(configs, acceptedRegexKey, null); String rejectedRegex = getString(configs, rejectedRegexKey, null); try { return new RegexFilter(acceptedRegex, rejectedRegex); } catch (PatternSyntaxException e) { throw new PropertiesModelLayerConfigurationRuntimeException.PropertiesModelLayerConfigurationRuntimeException_InvalidRegex( acceptedRegex + " or " + rejectedRegex); } } public Boolean idUsersSynchActivated() { LDAPUserSyncConfiguration config = getLDAPUserSyncConfiguration(false); return config != null && !(config.getDurationBetweenExecution() == null && CollectionUtils.isEmpty(config.getScheduleTime())); } public Date getNextUsersSyncFireTime() { return nextUsersSyncFireTime; } public void setNextUsersSyncFireTime(Date nextUsersSyncFireTime) { this.nextUsersSyncFireTime = nextUsersSyncFireTime; } }